<?php
/**
 * Nuwani PHP IRC Bot Framework
 * Copyright (c) 2006-2010 The Nuwani Project
 *
 * Nuwani is a framework for IRC Bots built using PHP. Nuwani speeds up bot 
 * development by handling basic tasks as connection- and bot management, timers
 * and module managing. Features for your bot can easily be added by creating
 * your own modules, which will receive callbacks from the framework.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @copyright Copyright (c) 2006-2010 The Nuwani Project
 * @package Nuwani
 * @author Peter Beverloo <peter@lvp-media.com>
 * @author Dik Grapendaal <dik.grapendaal@gmail.com>
 * @see http://nuwani.googlecode.com
 */

class Timer
{
        /**
         * This property contains all the timers which currently are running.
         * The per-timer array contains the start- and endtime and the callback.
         * 
         * @var array
         */
        
        private static $m_aRunningTimers = array ();
        
        /**
         * An alternative syntax for the Create function, so you can use
         * new Timer (..) instead of calling the Create function. Deprecated.
         * 
         * @param callback $aFunction Function you wish to call.
         * @param interval $nInterval Ideal interval in miliseconds, won't be accurate.
         * @param boolean $bRepeating Repeat this timer forever or stop it after?
         */
        
        public function __construct ($aFunction, $nInterval = 1000, $bRepeating = false)
        {
                trigger_error ('new Timer () has been deprecated. Use Timer :: Create () instead.', E_USER_DEPRECATED);
                self :: Create ($aFunction, $nInterval, $bRepeating);
        }
        
        /**
         * This function creates a new timer using the arguments as passed on
         * to the function. It runs using the Process function.
         * 
         * @param callback $aFunction Function you wish to call.
         * @param interval $nInterval Ideal interval in miliseconds, won't be accurate.
         * @param boolean $bRepeating Repeat this timer forever or stop it after?
         * @return string
         */
        
        public static function Create ($aFunction, $nInterval = 1000, $bRepeating = false)
        {
                $sTimerId   = substr (sha1 (time () . '-' . uniqid ()), 5, 10);
                $nInterval /= 1000;
                
                if (!is_callable ($aFunction))
                {
                        throw new Exception ('Could not create the timer due to the function not being callable.');
                        return false ;
                }
                
                self :: $m_aRunningTimers [] = array
                (
                        'TimerId'       => $sTimerId,
                        'Function'      => $aFunction,
                        'Interval'      => $nInterval,
                        'Repeating'     => $bRepeating,
                        'RunAt'         => microtime (true) + $nInterval
                );
                
                return $sTimerId;
        }
        
        /**
         * This function will stop running a certain timer given the timerID as
         * passed on as the first argument of this function in a very long
         * sentence.
         * 
         * @param string $sTimerId Timer ID that you wish to stop.
         * @return boolean
         */
        
        public static function Stop ($sTimerId)
        {
                foreach (self :: $m_aRunningTimers as $iTimerIndex => $aTimerInfo)
                {
                        if ($aTimerInfo ['TimerId'] == $sTimerId)
                        {
                                unset (self :: $m_aRunningTimers [$iTimerIndex], $aTimerInfo);
                                return true ;
                        }
                }
                
                return false ;
        }
        
        /**
         * The process function will check all the timers to see whether they
         * have to be executed, and if so, execute them accordingly.
         */
        
        public static function Process ()
        {
                $nCurrentTime = microtime (true);
                foreach (self :: $m_aRunningTimers as $iTimerIndex => & $aTimerInfo)
                {
                        if ($aTimerInfo ['RunAt'] < $nCurrentTime)
                        {
                                try
                                {
                                        call_user_func ($aTimerInfo ['Function']);
                                }
                                catch (Exception $pException)
                                {
                                        ErrorExceptionHandler :: getInstance () -> processException ($pException);
                                        @ ob_end_flush ();
                                }
                                
                                if ($aTimerInfo ['Repeating'])
                                {
                                        $aTimerInfo ['RunAt'] = microtime (true) + $aTimerInfo ['Interval'];
                                }
                                else
                                {
                                        unset (self :: $m_aRunningTimers [$iTimerIndex]);
                                }
                        }
                }
        }
        
        /**
         * This function can be used to get a list of all the timers which
         * currently are active in the system. The first argument can be used
         * to determain whether to filter the list and include only permanent
         * timers.
         * 
         * @param boolean $bOnlyPermanent Only permanent, repeating timers?
         * @return array
         */
        
        public static function getTimerList ($bOnlyPermanent = false)
        {
                if ($bOnlyPermanent)
                {
                        $aTimers = array ();
                        foreach (self :: $m_aRunningTimers as $aTimerInfo)
                        {
                                if ($aTimerInfo ['Repeating'] !== true)
                                {
                                        continue ;
                                }
                                
                                $aTimers [] = $aTimerInfo;
                        }
                        
                        return $aTimers;
                }
                
                return self :: $m_aRunningTimers;
        }
        
        /**
         * This function will return a certain option from one of the timers
         * that are controlled using this class. They can be changed using
         * setTimerOption.
         * 
         * @param string $sTimerId Timer ID to get the option of.
         * @param string $sOption Which option to retrieve from this timer?
         * @return mixed
         */
        
        public static function getTimerOption ($sTimerId, $sOption)
        {
                foreach (self :: $m_aRunningTimers as $iTimerIndex => $aTimerInfo)
                {
                        if ($aTimerInfo ['TimerId'] == $sTimerId)
                        {
                                if (!isset ($aTimerInfo [$sOption]))
                                {
                                        throw new Exception ('There is no option "' . $sOption . '" for timers.');
                                }
                                
                                return $aTimerInfo [$sOption];
                        }
                }
                
                throw new Exception ('No timer could be found with the ID "' . $sTimerId . '".');
        }
        
        /**
         * A fairly easy function which will change a setting related to one of
         * the timers in the system. Value should be valid, doesn't get checked
         * in here.
         * 
         * @param string $sTimerId Timer that you wish to change.
         * @param string $sOption The option that's about to be changed.
         * @param mixed $mValue Value to give to the option.
         * @return boolean
         */
        
        public static function setTimerOption ($sTimerId, $sOption, $mValue)
        {
                foreach (self :: $m_aRunningTimers as $iTimerIndex => $aTimerInfo)
                {
                        if ($aTimerInfo ['TimerId'] == $sTimerId)
                        {
                                if (!isset ($aTimerInfo [$sOption]))
                                {
                                        throw new Exception ('There is no option "' . $sOption . '" for timers.');
                                        return false ;
                                }
                                
                                $aTimerInfo [$sOption] = $mValue;
                                return true ;
                        }
                }
                
                throw new Exception ('No timer could be found with the ID "' . $sTimerId . '".');
        }

};

?>